package lockers

import (
	"fmt"
	"github.com/go-redis/redis/v7"
	"os"
	"time"
)

type RedisDisLocker struct {
	rds       *redis.Client
	lockValue string
}

func NewRedisDisLocker(rds *redis.Client) (DistributeLocker, error) {
	name, err := os.Hostname()
	if err != nil {
		name = fmt.Sprintf("unknow_%d", time.Now().UnixNano())
	}
	return &RedisDisLocker{
		rds:       rds,
		lockValue: "locked_on_" + name,
	}, nil
}

// ttl: 秒级
func (l *RedisDisLocker) Lock(key string, ttl int64) (bool, error) {
	_, err := l.rds.Do("set", key, l.lockValue, "NX", "EX", ttl).Result()
	if err == redis.Nil {
		return false, nil
	} else if err != nil {
		return false, err
	}
	return true, nil
}

func (l *RedisDisLocker) Release(key string) error {
	luaDelete := redis.NewScript(`if redis.call("get", KEYS[1]) == ARGV[1] then return redis.call("del", KEYS[1]) else return 0 end`)
	_, err := luaDelete.Run(l.rds, []string{key}, l.lockValue).Result()
	return err
}

// ttl: 秒级
func (l *RedisDisLocker) Delay(key string, ttl int64) error {
	luaRefresh := redis.NewScript(`if redis.call("get", KEYS[1]) == ARGV[1] then return redis.call("pexpire", KEYS[1], ARGV[2]) else return 0 end`)
	_, err := luaRefresh.Run(l.rds, []string{key}, l.lockValue, ttl*1000).Result()
	return err
}

// 返回 毫秒级
func (l *RedisDisLocker) TTL(key string) (int64, error) {
	luaPTTL := redis.NewScript(`if redis.call("get", KEYS[1]) == ARGV[1] then return redis.call("pttl", KEYS[1]) else return -3 end`)
	t, err := luaPTTL.Run(l.rds, []string{key}, l.lockValue).Result()
	return t.(int64), err
}
